import base_api

SET_STATE = 1
RESET_STATE = 2
CHECK_STATE = 3
PASS_STATE = 4
INIT_STATE = 5

FIRST_INC_BSLV = 0
FIRST_INC_WLV = 1


class AddrInfo():
    def __init__(self, rowStart=0, rowCount=1, colStart=0, colCount=1):
        assert rowCount > 0 and colCount > 0
        self.rowStart = rowStart
        self.rowCount = rowCount
        self.colStart = colStart
        self.colCount = colCount


class MapCellPara():
    def __init__(self):
        self.currentVal = 0
        self.targetVal = 0
        self.setVWL = 0
        self.setVBL = 0
        self.rstVWL = 0
        self.rstVSL = 0
        self.state = 0
        self.checkCnt = 0
        self.checkPassCnt = 0
        self.checkSetCnt = 0
        self.checkRstCnt = 0

    def printPara(self):
        print('********************************')
        print('currentVal %d' % self.currentVal)
        print('targetVal %d' % self.targetVal)
        print('setVWL %f' % self.setVWL)
        print('setVBL %f' % self.setVBL)
        print('rstVWL %f' % self.rstVWL)
        print('rstVSL %f' % self.rstVSL)
        print('state %d' % self.state)
        print('checkCnt %d' % self.checkCnt)
        print('checkPassCnt %d' % self.checkPassCnt)
        print('checkSetCnt %d' % self.checkSetCnt)
        print('checkRstCnt %d' % self.checkRstCnt)
        print('********************************')


class MapPara():
    def __init__(self):
        self.setVWLStart = 1.8
        self.setVWLStep = 0.2
        self.setVWLEnd = 3.2
        self.setVBLStart = 2.6
        self.setVBLStep = 0
        self.setVBLEnd = 2.6
        self.rstVWLStart = 3.0
        self.rstVWLStep = 0.2
        self.rstVWLEnd = 4.4
        self.rstVSLStart = 3.8
        self.rstVSLStep = 0
        self.rstVSLEnd = 3.8
        self.errorHigh = 1
        self.errorLow = 1
        self.maxProgramNum = 200
        self.maxCheckNum = 7
        self.checkThreshold = 5  # pass if checkPassCnt > checkThreshold


class Operation():
    def __init__(self):
        self.rramApi = base_api.BaseAPI()
        self.cellArray = [[MapCellPara() for _ in range(base_api.TOTAL_COL)]
                          for _ in range(base_api.TOTAL_ROW)]
        self.cellPassCnt = 0

    def initCellArr(self, addr: AddrInfo, mapPara: MapPara, targetArr):
        for row in range(addr.rowStart, addr.rowStart + addr.rowCount):
            for col in range(addr.colStart, addr.colStart + addr.colCount):
                self.cellArray[row][col].currentVal = 0
                self.cellArray[row][col].targetVal = targetArr[row][col]
                self.cellArray[row][col].setVWL = mapPara.setVWLStart
                self.cellArray[row][col].setVBL = mapPara.setVBLStart
                self.cellArray[row][col].rstVWL = mapPara.rstVWLStart
                self.cellArray[row][col].rstVSL = mapPara.rstVSLStart
                self.cellArray[row][col].state = INIT_STATE
                self.cellArray[row][col].checkCnt = 0
                self.cellArray[row][col].checkPassCnt = 0
                self.cellArray[row][col].checkSetCnt = 0
                self.cellArray[row][col].checkRstCnt = 0

    def updateCellPara(self, addr: AddrInfo, mapPara: MapPara):
        self.cellPassCnt = 0
        voltageIncMode = FIRST_INC_BSLV
        for row in range(addr.rowStart, addr.rowStart + addr.rowCount):
            for col in range(addr.colStart, addr.colStart + addr.colCount):
                cell = self.cellArray[row][col]
                saveState = cell.state
                if cell.state == PASS_STATE:
                    self.cellPassCnt += 1
                    continue
                cell.currentVal = self.rramApi.readOneCell_1(row, col)
                upper = min(cell.targetVal + mapPara.errorHigh, 15)
                lower = max(8, cell.targetVal - mapPara.errorLow)

                if cell.currentVal < lower:
                    if cell.state == CHECK_STATE:
                        cell.checkCnt += 1
                        cell.checkSetCnt += 1
                    else:
                        cell.state = SET_STATE
                elif cell.currentVal > upper:
                    if cell.state == CHECK_STATE:
                        cell.checkCnt += 1
                        cell.checkRstCnt += 1
                    else:
                        cell.state = RESET_STATE
                else:
                    if cell.state == CHECK_STATE:
                        cell.checkCnt += 1
                        cell.checkPassCnt += 1
                    else:
                        cell.state = CHECK_STATE
                        cell.checkCnt = 0
                        cell.checkPassCnt = 0
                        cell.checkSetCnt = 0
                        cell.checkRstCnt = 0

                if saveState == CHECK_STATE:
                    if cell.checkCnt >= mapPara.maxCheckNum:
                        if cell.checkPassCnt > mapPara.checkThreshold:
                            cell.state = PASS_STATE
                        elif cell.checkSetCnt > cell.checkRstCnt:
                            cell.state = SET_STATE
                        else:
                            cell.state = RESET_STATE

                if saveState == SET_STATE and cell.state == SET_STATE:
                    if voltageIncMode == FIRST_INC_BSLV:
                        cell.setVBL += mapPara.setVBLStep
                        if cell.setVBL >= mapPara.setVBLEnd:
                            cell.setVWL += mapPara.setVWLStep
                            cell.setVBL = mapPara.setVBLEnd

                        if cell.setVWL > mapPara.setVWLEnd:
                            cell.setVBL = mapPara.setVBLEnd
                            cell.setVWL = mapPara.setVWLEnd
                    else:
                        cell.setVWL += mapPara.setVWLStep
                        if cell.setVWL >= mapPara.setVWLEnd:
                            cell.setVBL += mapPara.setVBLStep
                            cell.setVWL = mapPara.setVWLStart

                        if cell.setVBL > mapPara.setVBLEnd:
                            cell.setVBL = mapPara.setVBLEnd
                            cell.setVWL = mapPara.setVWLEnd
                elif saveState == RESET_STATE and cell.state == RESET_STATE:
                    if voltageIncMode == FIRST_INC_BSLV:
                        cell.rstVSL += mapPara.rstVSLStep
                        if cell.rstVSL >= mapPara.rstVSLEnd:
                            cell.rstVWL += mapPara.rstVWLStep
                            cell.rstVSL = mapPara.rstVSLEnd

                        if cell.rstVWL > mapPara.rstVWLEnd:
                            cell.rstVSL = mapPara.rstVSLEnd
                            cell.rstVWL = mapPara.rstVWLEnd
                    else:
                        cell.rstVWL += mapPara.rstVWLStep
                        if cell.rstVWL >= mapPara.rstVWLEnd:
                            cell.rstVSL += mapPara.rstVSLStep
                            cell.rstVWL = mapPara.rstVWLStart
                        if cell.rstVSL > mapPara.rstVSLEnd:
                            cell.rstVSL = mapPara.rstVSLEnd
                            cell.rstVWL = mapPara.rstVWLEnd
                else:
                    cell.setVWL = mapPara.setVWLStart
                    cell.setVBL = mapPara.setVBLStart
                    cell.rstVWL = mapPara.rstVWLStart
                    cell.rstVSL = mapPara.rstVSLStart
            # cell.printPara()

    def cellOperate(self, addr: AddrInfo):
        setPulse = 1000
        resetPulse = 4000
        for row in range(addr.rowStart, addr.rowStart + addr.rowCount):
            for col in range(addr.colStart, addr.colStart + addr.colCount):
                cell = self.cellArray[row][col]
                if cell.state == SET_STATE:
                    self.rramApi.setOneCell_1(row, col, cell.setVBL,
                                              cell.setVWL, setPulse)
                elif cell.state == RESET_STATE:
                    self.rramApi.resetOneCell_1(row, col, cell.rstVSL,
                                                cell.rstVWL, resetPulse)
                else:
                    pass

    def map(self, addr: AddrInfo, mapPara: MapPara, targetArr):
        self.initCellArr(addr, mapPara, targetArr)
        opCnt = 0
        self.cellPassCnt = 0
        cellNum = addr.rowCount * addr.colCount
        while opCnt < mapPara.maxProgramNum and self.cellPassCnt < cellNum:
            self.updateCellPara(addr, mapPara)
            self.cellOperate(addr)
            opCnt += 1
            print('opCnt = %d, passCnt = %d' % (opCnt, self.cellPassCnt))
